Add Entire blame and why commands#1305
Conversation
There was a problem hiding this comment.
Pull request overview
Adds two new CLI commands—entire blame and entire why—to attribute current file lines to Entire checkpoints (via git blame --line-porcelain) and to explain the checkpoint/prompt context behind a specific file/line, with optional JSON output.
Changes:
- Introduces
entire blame <file> [--line N|N-M] [--json]andentire why <file[:line]> [--json]. - Implements attribution resolution that maps blamed commits to
Entire-Checkpointtrailers and checkpoint/session metadata. - Adds unit tests for blame porcelain parsing, line range parsing, and JSON/output behavior; updates README command table/docs.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| README.md | Documents the new blame and why commands and adds brief usage context. |
| cmd/entire/cli/root.go | Registers the new blame and why Cobra commands on the root command. |
| cmd/entire/cli/attribution.go | Implements the attribution pipeline, rendering, JSON output, and command handlers for blame/why. |
| cmd/entire/cli/attribution_test.go | Adds unit tests covering parsing and output behaviors for the new attribution functionality. |
| result, err := resolveFileAttribution(ctx, file, false) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| if lineRange != nil { | ||
| result.Lines = filterAttributionLines(result.Lines, *lineRange) | ||
| result.Summary = summarizeAttributionLines(result.Lines) | ||
| } |
| func runGitBlame(ctx context.Context, repoRoot, file string) ([]rawBlameLine, error) { | ||
| cmd := exec.CommandContext(ctx, "git", "-C", repoRoot, "blame", "--line-porcelain", "--", file) | ||
| var stderr bytes.Buffer | ||
| cmd.Stderr = &stderr | ||
| out, err := cmd.Output() | ||
| if err != nil { | ||
| msg := strings.TrimSpace(stderr.String()) | ||
| if msg == "" { | ||
| msg = err.Error() | ||
| } | ||
| return nil, fmt.Errorf("git blame failed for %s: %s", file, msg) | ||
| } |
| fmt.Fprintf(w, "\n %s %d in %s\n", sty.render(sty.bold, "Line"), line.LineNumber, sty.render(sty.bold, file)) | ||
| if line.Content != "" { | ||
| fmt.Fprintf(w, " %s\n\n", sty.render(sty.dim, strings.TrimSpace(line.Content))) | ||
| } |
| if len(candidates) > 1 { | ||
| line.Candidates = candidates | ||
| } else if len(candidates) == 1 { | ||
| line.Candidates = candidates | ||
| } |
b479d9a to
0ef2a90
Compare
|
Updated this PR to address the attribution review feedback:
Validation rerun:
|
| return parseBlamePorcelain(string(out)) | ||
| } | ||
|
|
||
| var blameHeaderRe = regexp.MustCompile(`^([0-9a-f]{40})\s+\d+\s+(\d+)(?:\s+\d+)?$`) |
There was a problem hiding this comment.
This would not work for SHA-256 repos which would be 64 chars. Probably good to add support for both.
| } | ||
| continue | ||
| } | ||
| if ctx, ok := resolver.checkpointCache[candidate.CheckpointID]; ok { |
There was a problem hiding this comment.
This shadows the function parameter ctx, it's not an issue but maybe we should use a different name here.
Soph
left a comment
There was a problem hiding this comment.
Thanks! I did run /simplify with claude code that removed some duplication and then also two other things I flagged as comments.
Entire-Checkpoint: c80cff869e76
8094537 to
2e14aab
Compare
|
Updated again and kept Soph's Fixes included:
Validation rerun on the rebased branch:
|
5beab2d to
604941a
Compare
604941a to
6ecc60c
Compare
|
Pushed the compact blame alignment fix. Root cause:
What changed:
Validation rerun:
|
Mark `entire blame` and `entire why` as Hidden during maturation, matching the pattern used by review/investigate/org/project/repo/grant. Register both in the labs experimental-command registry so they're discoverable via `entire labs`, and reword the README to point users there instead of listing them in the main command table. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 0c07b873709c
… rule Two related correctness bugs in line attribution: 1. `entire blame` and `entire why` disagreed on the tag. resolveLine marks a line [MX] only when its *preferred* candidate is mixed, but the why-time enrichment path (enrichAttributionLineWithFetch) marked it [MX] if *any* candidate was mixed. A line with a non-mixed preferred candidate and a mixed secondary one rendered [AI] in blame and [MX] in why. Extract a single authorshipForPreferred() rule used by both paths. 2. Mixed was checkpoint-scoped, not session-scoped. readCheckpointContext OR-combined the checkpoint-wide CombinedAttribution with every session's attribution, so a line from an agent-only file got tagged [MX] whenever the checkpoint *also* touched a hand-edited file. Scope Mixed to the session whose work actually touched this file; fall back to the checkpoint-wide attribution only when no session metadata resolves. Adds a regression test where the file-touching session is pure-AI but the checkpoint's combined attribution is mixed — the line must stay [AI]. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: ef513bf77157
attributionCandidate and attributionCheckpointContext declared identical field lists with identical JSON tags, kept in sync only by a candidateFromContext() cast. Adding a field meant editing both. Alias attributionCandidate to attributionCheckpointContext so there is one definition, and drop the now-noop conversion helper. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 91b11a441843
Two silent-degradation paths in readCheckpointContext gave the user false confidence: - When a blamed file matched none of a checkpoint's sessions (e.g. it was renamed after the checkpoint), session selection silently fell back to the first session and presented its agent/prompt as exact. Track whether the match was by file, and when it was a multi-session fallback set a new SessionFallback flag; `entire why` now prints a "may have been renamed" hint. - When a checkpoint's sessions all failed to read (summary present, per-session metadata unreadable), the line kept a confident [AI] tag with blank agent/model/prompt and no indication. Set MetadataMissing in that case so the existing "trailer-level attribution only" hint shows and the why path retries via remote fetch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 7f9d62c609b2
renderAttributionBlameCompact and renderAttributionBlameLong each repeated the same header print, empty-file short-circuit, and trailing summary call around their column layouts. Pull that scaffolding into renderAttributionBlameTable and pass each variant's table body as a callback. Output is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 5fc3dec002a5
The summary computed AI/Human/Mixed percentages by independent integer division, so they drifted from a coherent whole — e.g. one line each of AI, Human, and Mixed rendered 33% / 33% / 33% = 99%. Apportion with the largest-remainder (Hamilton) method across all four buckets (including uncommitted, which shares the 100% but is shown only as a count) so the visible figures stay coherent. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: e2b9528e57db
Summary
Adds first-class
entire blameandentire whycommands for AI-aware line attribution.What changed
entire blame <file> [--line N|N-M] [--json].entire why <file[:line]> [--json].git blame --line-porcelain.Entire-Checkpointtrailers plus checkpoint/session metadata fromentire/checkpoints/v1.[AI],[HU],[MX], or[??]for uncommitted lines.checkpoint explainhint for a specific line.Why
After an AI agent edits a repo,
git blamecan show the commit, but not why the line exists. These commands connect a line back to the Entire checkpoint and original agent prompt, so users can inspect AI-authored code without manually chasing commit trailers and checkpoint metadata.Validation
go test ./cmd/entire/cli -run 'Test(ParseBlamePorcelain|ParseAttributionLineRange|Attribution)' -count=1go test ./cmd/entire/cli -run TestAttribution -count=1go test ./cmd/entire/cli/checkpoint ./cmd/entire/cli/trailers -count=1go build -o /tmp/entire-blame-why-clean ./cmd/entirego test ./...go vet ./cmd/entire/cli/...mise run lintmise run test:ci:coremise run test:e2e:canaryCanary result: 59/59 vogon tests passed and 4/4 roger-roger tests passed.
Manual validation on a real Entire-enabled repo:
/Users/suhaan/Documents/Ultron.entire blame references/entire-checkpointing.md --line 60-70showed mixed[AI]and[HU]attribution.entire why references/entire-checkpointing.md:68resolved the line to Codex, modelgpt-5.5, checkpoint9a91ce5c55f2, session019e6ba4, commita77cd651, and the original prompt.